package br.com.betioli.jme.games.sokobob.level;

import br.com.betioli.jme.games.sokobob.config.GameConfig;
import br.com.betioli.jme.games.sokobob.layer.BackLayer;
import br.com.betioli.jme.games.sokobob.layer.TargetLayer;
import br.com.betioli.jme.games.sokobob.layer.WallLayer;
import br.com.betioli.jme.games.sokobob.sprite.BobActor;
import br.com.betioli.jme.games.sokobob.sprite.BoneActor;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.LayerManager;

public class Level {

    private int levelIndex;
    private char[][] map;
    private int numberOfBoxes;
    
    private BobActor bob;
    private BoneActor[] bones;
    private WallLayer walls;
    private TargetLayer targets;
    private BackLayer background;
    private LayerManager layerManager;
    
    public Level(int levelIndex, char[][] map, int numberOfBoxes) {

        this.levelIndex = levelIndex;
        this.map = map;
        this.numberOfBoxes = numberOfBoxes;
        
        loadLayers();
        loadSprites();
        buildLevel();
    }

    /**
     * Carrega as layers
     */
    private void loadLayers() {
        targets    = new TargetLayer(map);
        walls      = new WallLayer(map);
        background = new BackLayer(map);
    }

    /**
     * Define as posições das sprites com base nas informações carregadas do 
     * arquivo level.x
     */
    private void loadSprites() {

        //Inicializa Sprites
        
        bones = new BoneActor[numberOfBoxes];

        int boxCount = 0;
        for (int r = 0; r < GameConfig.ROWS; r++) {
            for (int c = 0; c < GameConfig.COLUMNS; c++) {

                if ((map[c][r] == GameConfig.BOB_REF) || (map[c][r] == GameConfig.BOB_IN_TARGET_REF) ) {
                    bob = new BobActor();
                    bob.setPosition(c, r);

                } else if ((map[c][r] == GameConfig.BONE_REF) || (map[c][r] == GameConfig.BOX_IN_TARGET_REF)) {
                    bones[boxCount] = new BoneActor();
                    bones[boxCount].setPosition(c, r);
                    boxCount++;
                }
            }
        }
    }

    /**
     * Constrói as camadas do jogo (bob, bone, target, wall e background)
     */
    private void buildLevel() {
        
        layerManager = new LayerManager();
        
        for (int i = 0; i < getBoneSprite().length; i++) {
            layerManager.append(getBoneSprite()[i]);
        }
        
        layerManager.append(getBobSprite());
        layerManager.append(getWallLayer());
        layerManager.append(getTargetLayer());
        layerManager.append(getBackground());
    }

    /**
     * Desenha o estado atual do jogo na tela.
     */
    public void paint(Graphics g, int x, int y) {
        layerManager.paint(g, x, y);
    }

    /**
     * Realiza a checagem de colisão entre:
     *  - jogador e parede
     *  - jogador e objeto
     *  - objeto e parede
     *  - objeto e objeto
     * 
     * Se necessário, o movimento é desfeito.
     */
    public void checkCollision() {

        if (bob.collidesWith(walls, false)) {
            bob.undo();

        } else {
            outer:
            for (byte i = 0; i < bones.length; i++) {

                if (bob.collidesWith(bones[i], false)) {

                    bones[i].move( bob.getLastMove() );

                    if (bones[i].collidesWith(walls, false)) {
                        bones[i].undo();
                        bob.undo();
                        break outer;
                    }

                    for (byte j = 0; j < bones.length; j++) {
                        if (i != j && bones[i].collidesWith(bones[j], false)) {
                            bones[i].undo();
                            bob.undo();
                            break outer;
                        }
                    }
                    
                }
            }
        }
    }

    /**
     * Verifica se a condição de sucesso no level foi atingida, verificando se
     * todos os ossos (bones) estão colidino com a layer dos buracos (target).
     * 
     * @return true se a condição de sucesso foi atingida.
     *         false caso contário
     */
    public boolean verifyWinCondition() {
        boolean winCondition = true;

        for (byte i = 0; i < bones.length; i++) {
            if ( ! bones[i].collidesWith(targets, false)) {
                winCondition = false;
                break;
            }
        }

        return winCondition;
    }

    //Getters
    public BobActor getBobSprite() {
        return bob;
    }

    public BoneActor[] getBoneSprite() {
        return bones;
    }

    public WallLayer getWallLayer() {
        return walls;
    }

    public TargetLayer getTargetLayer() {
        return targets;
    }

    public int getLevelIndex() {
        return levelIndex;
    }

    public BackLayer getBackground() {
        return background;
    }
}
